'''
KML style classes

Created on Sep 28, 2010

@author: morgan
'''
from __xml__ import createElement, xml
from base import KMLObject, Color
from enum import colorMode, displayMode
from data import Icon, hotSpot

__all__ = ['SubStyle', 'BalloonStyle', 'ColorStyle', 'LineStyle', 'IconStyle', 'Style']

class SubStyle(KMLObject):
    """
    This is the base class of all *Style objects. It is a metaclass and should 
    not be used directly. 
    """
    def __init__(self, id=""):
        KMLObject.__init__(self, id)
  
class BalloonStyle(SubStyle):
    """
    Specifies how the description balloon for placemarks is drawn. The
    <bgColor>, if specified, is used as the background color of the balloon.
    See <Feature> for a diagram illustrating how the default description
    balloon appears in Google Earth.

    *Object Attributes*:
        /bgcolor/:
            Background color of the balloon (optional). Color and opacity
            (alpha) values are expressed in hexadecimal notation. The range of
            values for any one color is 0 to 255 (00 to ff). The order of
            expression is aabbggrr, where aa=alpha (00 to ff); bb=blue (00 to
            ff); gg=green (00 to ff); rr=red (00 to ff). For alpha, 00 is fully
            transparent and ff is fully opaque. For example, if you want to
            apply a blue color with 50 percent opacity to an overlay, you would
            specify the 0x7fff0000, where alpha=0x7f, blue=0xff, green=0x00, 
            and red=0x00. The default is opaque white (0xffffffff).
        /textColor/:
            Foreground color for text. The default is black (ff000000).
        /text/:
            Text displayed in the balloon. If no text is specified, Google Earth
            draws the default balloon (with the Feature <name> in boldface, the
            Feature <description>, links for driving directions, a white
            background, and a tail that is attached to the point coordinates of
            the Feature, if specified).

            You can add entities to the <text> tag using the following format to
            refer to a child element of Feature: $[name], $[description],
            $[address], $[id], $[Snippet]. Google Earth looks in the current
            Feature for the corresponding string entity and substitutes that
            information in the balloon. To include To here - From here driving
            directions in the balloon, use the $[geDirections] tag. To prevent
            the driving directions links from appearing in a balloon, include
            the <text> element with some content, or with $[description] to
            substitute the basic Feature <description>. 
        
            For example, in the following KML excerpt, *$[name]* and
            *$[description]* fields will be replaced by the /name/ and
            /description/ fields found in the Feature objects that use this
            BalloonStyle:

            BalloonStyle.text = "This is $[name], whose description is: "+
                    "$[description]"
    """
    def __init__(self, id ="", bgColor=0xffffffff, textColor=0xff000000,
                 text=None, displayMode=displayMode.default):
        self.__tagName__ = "BalloonStyle"
        SubStyle.__init__(self, id)
        self.bgColor = bgColor
        self.textColor = textColor
        self.text=text
        self.displayMode = displayMode 

    @property
    def displayMode(self):
        if hasattr(self, "_displayMode"):
            return self._displayMode
        else:
            return None

    @displayMode.setter
    def displayMode(self, value):
        if type(value) != str and not(isinstance(value, displayMode)):
            raise TypeError("Expecting value to be a str object or a "+\
                            "displayMode class object")
        if type(value) == str:
            self._displayMode = str(displayMode(value))
        else:
            self._displayMode = str(value)
    
    @property
    def text(self):
        return self._text
    
    @text.setter
    def text(self, value):
        if not value:
            self._text = ""
            self._cdata = None
        elif type(value) == str:
            self._text = value
            self._cdata = xml.CDATASection()
            self._cdata._set_data(value)
        elif isinstance(value, xml.CDATASection):
            self._text=value.toxml("UTF-8")
            self._cdata=value
        else:
            raise TypeError("Snippet was expecting a value of string or"+\
                            " xml.dom.minidom.CDATASection")        

    @property
    def bgColor(self):
        return self._bgColor

    @bgColor.setter
    def bgColor(self, value):
        minValue = 0
        maxValue = 0xffffffff
        if type(value) == Color:
            self._bgColor = value.value
        elif type(value) != int:
            raise TypeError("Expecting value to be an integer (hex)")
        elif (value < minValue) or (value > maxValue):
            raise ValueError("Value should be a postive integer, " +
                    "no larger than 0xffffffff (%s)" % str(maxValue))
        else:
            self._bgColor = value

    @property  
    def textColor(self):
        return self._textColor    
    
    @textColor.setter
    def textColor(self, value):
        minValue = 0
        maxValue = 0xffffffff
        if type(value) == Color:
            self._textColor = value.value
        elif type(value) != int:
            raise TypeError("Expecting value to be an integer (hex) or kml.Color")
        elif (value < minValue) or (value > maxValue):
            raise ValueError("Value should be a positive integer, " +
                    "no larger than 0xffffffff (%s)" % str(maxValue))
        else:
            self._textColor = value

    def toXmlNode(self, kml):
        myNode = SubStyle.toXmlNode(self, kml)
        myNode.appendChild(createElement(kml._doc, "bgColor", 
                                         str(Color(value=self.bgColor))))
        myNode.appendChild(createElement(kml._doc, "textColor",
                                         str(Color(value=self.textColor))))
        if self._text:
            myNode.appendChild(createElement(kml._doc, "text", self._text))
        mode = displayMode(self.displayMode)
        myNode.appendChild(mode.toXmlNode(kml))
        return myNode
        
    def parseKMLNode(self,KMLNode):
        SubStyle.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("bgColor")
        if elems:
            self.bgColor = Color(value=elems[0].childNodes[0].data)

        elems = KMLNode.getElementsByTagName("textColor")
        if elems:
            self.textColor = Color(value=elems[0].childNodes[0].data)

        elems = KMLNode.getElementsByTagName("text")
        if elems:
            self.text = elems[0].childNodes[0].data
            
        elems = KMLNode.getElementsByTagName("displayMode")
        if elems:          
            self.displayMode = elems[0].childNodes[0].data 
        
class ColorStyle(SubStyle):
    """
    This is an abstract element class and cannot be used directly in a KML file.
    It provides elements for specifying the color and color mode of extended
    style types.
    """
    def __init__(self, id="", color=0xffffffff, colorMode = colorMode.default):
        SubStyle.__init__(self, id)
        self.color = color
        self.colorMode = colorMode

    @property
    def colorMode(self):
        return self._colorMode

    @colorMode.setter
    def colorMode(self, value):
        if type(value) != str and not(isinstance(value, colorMode)):
            raise TypeError("ColorStyle is expecting value to be a string or "+
                            "a colorMode instance")
        if type(value) == str:
            self._colorMode = str(colorMode(value))
        else:
            self._colorMode = str(value)
    
    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, value):
        if type(value) not in [int, Color]:
            raise TypeError("Expecting value to be an integer (hex) or a kml.Color instance.")
        if type(value) == int:
            self._color = Color(value=value).value
        else:
            self._color = value.value
    
    def toXmlNode(self, kml):
        node = SubStyle.toXmlNode(self, kml)
        if self.color != 0xffffffff:
            node.appendChild(createElement(kml._doc,"color", 
                                           str(Color(value=self.color))))
        if self.colorMode != colorMode.default:
            node.appendChild(self._colorMode.toXmlNode(kml))
        return node
    
    def parseKMLNode(self, KMLNode):
        SubStyle.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("color")
        if elems:
            self.color = int(elems[0].childNodes[0].data, 16)
        elems = KMLNode.getElementsByTagName("colorMode")
        if elems:
            self.colorMode = elems[0].childNodes[0].data.strip()
        
class LineStyle(ColorStyle):
    def __init__(self, id="", width=1, *args, **kwdargs):
        self.__tagName__ = 'LineStyle'
        ColorStyle.__init__(self, id, *args, **kwdargs)
        self.width=width
        
    @property
    def width(self):
        return self._width
    
    @width.setter
    def width(self, value):
        if type(value) != int:
            raise TypeError("Expecting width to be an integer.")
        elif value < 0:
            raise ValueError("Expecting width to be positive.")
        else:
            self._width = value
            
    def toXmlNode(self, kml):
        node = ColorStyle.toXmlNode(self, kml)
        node.appendChild(createElement(kml._doc, "width", self.width))
        return node
    
    def parseKMLNode(self, KMLNode):
        ColorStyle.parseKMLNode(self, KMLNode)
        elems=KMLNode.getElementsByTagName("width")
        if elems:
            self.width = int(elems[0].childNodes[0].data) 
            
class PolyStyle(ColorStyle):
    def __init__(self, id="", fill=True, outline=True, *args, **kwds):
        pass

class IconStyle(ColorStyle):
    """
    *Description*:
        Specifies how icons for point Placemarks are drawn, both in the Places
        panel and in the 3D viewer of Google Earth. The /Icon/ element
        specifies the icon image. The scale element specifies the x, y
        scaling of the icon. The color specified in the /color/ element of
        /IconStyle/ is blended with the color of the /Icon/.

    *Object Attributes inherited from /ColorStyle/*:
        /color/ 
        /colorMode/

    *Object Attributes Specific to IconStyle:*
        /scale/:
            Resizes the icon
        /heading/:
            Direction (that is, North, South, East, West), in degrees.
            Default=0 (North). Values range from 0 to 360 degrees.
        /Icon/:
            An Icon object. In /IconStyle/, the only child element allowed
            of an Icon is /href/.
    """
    def __init__(self, id="", scale=1, heading=0, icon=None, hotSpot = None,
                 *args, **kwdargs): 
        self.__tagName__ = "IconStyle"
        self.scale = scale
        self.heading = heading
        self.Icon = icon
        self.hotSpot = hotSpot
        ColorStyle.__init__(self, id, *args, **kwdargs)

    @property
    def scale(self):
        return self._scale
    
    @scale.setter
    def scale(self, value):
        if type(value) != float and type(value) != int:
            raise TypeError("IconStyle expects scale to be a number.")
        elif value < 0:
            raise ValueError("IconStyle expects scale to be positive.")
        else:
            self._scale = value

    @property
    def heading(self):
        return self._heading 

    @heading.setter
    def heading(self, value):
        minValue = 0
        maxValue = 360
        if type(value) != float and type(value) != int:
            raise TypeError("IconStyle expects heading to be a number.")
        elif value < minValue or value > maxValue:
            raise ValueError("IconStyle expects heading to be between 0 and "+\
                "360.")
        else:
            self._heading = float(value)
    
    @property
    def Icon(self):
        if hasattr(self, "_Icon"):
            return self._Icon
        else:
            return None

    @Icon.setter
    def Icon(self, value):
        if value is None:
            if hasattr(self, "_Icon"):
                del self._Icon
        elif type(value) == Icon:
            self._Icon = value
        elif type(value) == str:
            self._Icon = Icon(href="value")
        else:
            raise TypeError("Icon must be a kml.Icon or a string containing an Icon.href value or None.")
    
    @property
    def hotSpot(self):
        if hasattr(self, "_hotSpot"):
            return self._hotSpot
        else:
            return None

    @hotSpot.setter
    def hotSpot(self, value):
        if value is None:
            if hasattr(self, "_hotSpot"):
                del self._hotSpot
        elif type(value) == hotSpot:
            self._hotSpot = value
        else:
            raise TypeError("hotSpot must be kml.hotSpot instance!")
    
    @hotSpot.deleter
    def hotSpot(self):
        del self._hotSpot
        
    def toXmlNode(self,kml):
        myNode = ColorStyle.toXmlNode(self, kml)
        if self.scale != 1:
            myNode.appendChild(createElement(kml._doc,"scale", self.scale))
        if self.heading != 0:
            myNode.appendChild(createElement(kml._doc,"heading", self.heading))
        if self.Icon:
            myNode.appendChild(self.Icon.toXmlNode(kml))
        if self.hotSpot:
            myNode.appendChild(self.hotSpot.toXmlNode(kml)) 
        return myNode

    def parseKMLNode(self,KMLNode):
        ColorStyle.parseKMLNode(self, KMLNode)
        elems = KMLNode.getElementsByTagName("scale")
        if elems:
            self.scale = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("heading")
        if elems:
            self.heading = float(elems[0].childNodes[0].data)
        elems = KMLNode.getElementsByTagName("Icon") 
        if elems:
            self.Icon = Icon()
            self.Icon.parseKMLNode(elems[0])
        elems = KMLNode.getElementsByTagName("hotSpot")  
        if elems:
            self.hotSpot = hotSpot()
            self.hotSpot.parseKMLNode(elems[0])

class Style(KMLObject):
    """
    A Style defines an addressable style group that can be referenced by
    StyleMaps and Features. Styles affect how Vector is presented in the 3D
    viewer and how Features appear in the Places panel of the List view. Shared
    styles are collected in a <Document> and must have an id defined for them so
    that they can be referenced by the individual Features that use them.
    """

    def __init__(self, id="", IconStyle=None, LabelStyle=None, LineStyle=None,
                 PolyStyle=None, BalloonStyle=None, ListStyle=None):
        self.__tagName__ = "Style"
        KMLObject.__init__(self, id)
        self._IconStyle = IconStyle
        self._LabelStyle = LabelStyle 
        self._LineStyle = LineStyle
        self._PolyStyle = PolyStyle
        self._BalloonStyle = BalloonStyle
        self._ListStyle = ListStyle

    def _setIconStyle(self, value):
        if type(value) != IconStyle:
            raise TypeError("Expecting value to be an IconStyle class object")
        self._IconStyle = value

    def _getIconStyle(self):
        return self._IconStyle

    IconStyle = property(_getIconStyle, _setIconStyle)

    def _setBalloonStyle(self, value):
        if type(value) != BalloonStyle:
            raise TypeError("Expecting value to be an BalloonStyle class object")
        self._BalloonStyle = value

    def _getBalloonStyle(self):
        return self._BalloonStyle

    BalloonStyle = property(_getBalloonStyle, _setBalloonStyle)
    
    def toXmlNode(self, kml):
        myNode = KMLObject.toXmlNode(self, kml)
        myNode.setAttribute("id", self.id)
        if self._BalloonStyle:
            myNode.appendChild(self._BalloonStyle.toXmlNode(kml))
        if self._IconStyle:
            myNode.appendChild(self._IconStyle.toXmlNode(kml))
        return myNode
    
    def parseKMLNode(self, KMLNode):
        KMLObject.parseKMLNode(self, KMLNode)
        if KMLNode.getElementsByTagName("BalloonStyle"):          
            self._BalloonStyle = BalloonStyle()
            self._BalloonStyle.parseKMLNode(KMLNode.getElementsByTagName("BalloonStyle")[0])
        if KMLNode.getElementsByTagName("IconStyle"):
            self._IconStyle = IconStyle()
            self._IconStyle.parseKMLNode(KMLNode.getElementsByTagName("IconStyle")[0])
